package com.hhit.zhgd.service.impl

import cn.afterturn.easypoi.excel.ExcelImportUtil
import cn.afterturn.easypoi.excel.entity.ImportParams
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult
import cn.hutool.core.collection.CollUtil
import cn.hutool.core.date.DateUtil
import cn.hutool.core.util.ObjectUtil
import cn.hutool.core.util.StrUtil
import cn.hutool.poi.excel.ExcelReader
import cn.hutool.poi.excel.ExcelUtil
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.metadata.IPage
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
import com.hhit.zhgd.component.FileProperties
import com.hhit.zhgd.dao.ZhgdArchiveSituationMapper
import com.hhit.zhgd.domain.dto.BackStageAddArchiveFileMenuDTO
import com.hhit.zhgd.domain.dto.BackStageArchiveSituationDTO
import com.hhit.zhgd.domain.entity.ZhgdArchiveSituation
import com.hhit.zhgd.domain.excelimport.ArchiveSituationImportNewParam
import com.hhit.zhgd.domain.excelimport.CatalogHierarchyBuilder
import com.hhit.zhgd.domain.excelimport.CatalogHierarchyBuilder.buildCatalogHierarchyFromDB
import com.hhit.zhgd.domain.excelimport.CatalogNode
import com.hhit.zhgd.domain.response.BaseResp
import com.hhit.zhgd.service.ZhgdArchiveSituationService
import jakarta.annotation.Resource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.util.stream.Collectors

@Service("zhgdArchiveSituationService")
class ZhgdArchiveSituationServiceImpl : ZhgdArchiveSituationService,
    ServiceImpl<ZhgdArchiveSituationMapper, ZhgdArchiveSituation>() {

    @Autowired
    private val zhgdArchiveSituationMapper: ZhgdArchiveSituationMapper? = null

    @Resource
    private val fileProperties: FileProperties? = null

    @Transactional(rollbackFor = [Exception::class])
    override fun importArchiveFile(multipartFile: MultipartFile?): BaseResp<Any?> {
        this.remove(null)
        // 文件处理成io流
        val inputStream = multipartFile!!.inputStream
        // io流给ExcelReader
        val excelReader: ExcelReader = ExcelUtil.getReader(inputStream)
        val list: List<List<Any>> = excelReader.read(2)
        val zhgdArchiveSituations: MutableList<ZhgdArchiveSituation> = CollUtil.newArrayList()
        for (row in list) {
            if (StrUtil.isNotBlank(row[1].toString())) {
                val zhgdArchiveSituation = ZhgdArchiveSituation(
                    typeNum = row[0].toString(),
                    type = row[1].toString(),
                    fileName = row[2].toString()
                )
                zhgdArchiveSituations.add(zhgdArchiveSituation)
            }
        }
        val saveRes = this.saveBatch(zhgdArchiveSituations)
        return if (saveRes) {
            BaseResp.success()
        } else {
            BaseResp.fail("上传归档文件失败,原因是保存数据库失败!")
        }
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun importArchiveFileNew(multipartFile: MultipartFile?): BaseResp<Any?> {
        this.remove(QueryWrapper())
        val params = ImportParams()
        // 表头设置为1行
        params.setHeadRows(1)
        val result: ExcelImportResult<ArchiveSituationImportNewParam> = ExcelImportUtil.importExcelMore(
            multipartFile!!.inputStream,
            ArchiveSituationImportNewParam::class.java, params
        )
        if (CollUtil.isNotEmpty(result.failList)) {
            for (archiveSituationImportNewParam in result.failList) {
                val msg =
                    "第" + ((archiveSituationImportNewParam.rowNum?:0) + 1) + "行的错误是：" + archiveSituationImportNewParam.errorMsg
                return BaseResp.fail("导入失败:$msg")
            }
        }
        val archiveSituationImportNewParams: List<ArchiveSituationImportNewParam> = result.getList()
        // 补全缺失的一级目录
        // 补全缺失的一级目录
        var parentName = StrUtil.EMPTY
        for (archiveSituationImportNewParam in archiveSituationImportNewParams) {
            if (StrUtil.isNotBlank(archiveSituationImportNewParam.oneLevelCatalog)) {
                parentName = archiveSituationImportNewParam.oneLevelCatalog?:""
            }
            archiveSituationImportNewParam.oneLevelCatalog = parentName
        }
        val catalogNodes: List<CatalogNode> =
            CatalogHierarchyBuilder.buildCatalogHierarchy(archiveSituationImportNewParams)!!
        // 遍历catalogNodes并写入数据库
        for (node in catalogNodes) {
            writeToCatalogData(node, -1)
        }
        return BaseResp.success()
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun importArchiveSingleFile(multipartFile: MultipartFile?, id: Int): BaseResp<Any?> {
        if (ObjectUtil.isNull(id)) {
            return BaseResp.fail("请选择对应的归档目录!")
        }
        if (multipartFile!!.isEmpty) {
            return BaseResp.fail("上传文件不能为空!")
        }
        // 获取文件名
        val fileName = multipartFile!!.originalFilename
        // 将文件写入nginx映射的存储路径
        val filePath: String = (fileProperties?.parentPath + fileProperties?.path) + fileName
        val file = File(filePath)
        multipartFile?.transferTo(file)
        val fileAddress: String = filePath.replace(fileProperties?.parentPath?:"", "")
        val zhgdArchiveSituation = ZhgdArchiveSituation()
        zhgdArchiveSituation.parentId = id
        zhgdArchiveSituation.type = fileName
        zhgdArchiveSituation.fileName = fileName
        zhgdArchiveSituation.fileAddress = fileAddress
        val saveRes = save(zhgdArchiveSituation)
        return if (saveRes) {
            BaseResp.success()
        } else {
            BaseResp.fail("上传失败!")
        }
    }

    override fun getArchiveFileInfo(): List<CatalogNode?>? {
        val zhgdArchiveSituations =
            list(QueryWrapper())
        return buildCatalogHierarchyFromDB(zhgdArchiveSituations)
    }

    override fun getArchiveFileMenuList(): List<CatalogNode?>? {
        val zhgdArchiveSituations =
            list(QueryWrapper<ZhgdArchiveSituation>().isNull("file_name"))
        return buildCatalogHierarchyFromDB(zhgdArchiveSituations)
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun addArchiveFileMenu(backStageAddArchiveFileMenuDTO: BackStageAddArchiveFileMenuDTO?): BaseResp<Any?> {
        val archiveSituation = ZhgdArchiveSituation()
        archiveSituation.type = backStageAddArchiveFileMenuDTO!!.menuName
        archiveSituation.parentId = backStageAddArchiveFileMenuDTO.parentId
        if (backStageAddArchiveFileMenuDTO.rootFlag == 1) {
            archiveSituation.parentId = -1
        }
        val saveRes = save(archiveSituation)
        return if (saveRes) {
            BaseResp.success()
        } else BaseResp.fail("新增目录菜单失败!")
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun editArchiveFileMenu(backStageAddArchiveFileMenuDTO: BackStageAddArchiveFileMenuDTO?): BaseResp<Any?> {
        val zhgdArchiveSituation = getById(backStageAddArchiveFileMenuDTO?.id)
        zhgdArchiveSituation.type = backStageAddArchiveFileMenuDTO?.menuName
        val updateRes = updateById(zhgdArchiveSituation)
        return if (updateRes) {
            BaseResp.success()
        } else {
            BaseResp.fail("修改目录菜单失败!")
        }
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun delArchiveFileMenu(id: Int?): BaseResp<Any?> {
        return if (ObjectUtil.isNotNull(id)) {
            // 查询该节点下是否有子节点
            val list = this.list(LambdaQueryWrapper<ZhgdArchiveSituation>().eq(ZhgdArchiveSituation::parentId, id))
            if (CollUtil.isNotEmpty(list)) {
                return BaseResp.fail("删除目录菜单失败!该菜单下还有其他子菜单")
            }
            val delRes = this.removeById(id)
            if (delRes) {
                BaseResp.success()
            } else {
                BaseResp.fail("删除目录菜单失败!")
            }
        } else {
            BaseResp.fail("无可删除的数据!")
        }
    }

    override fun getArchiveFilePageInfo(
        id: Int?,
        keyWord: String?,
        page: Int,
        size: Int
    ): IPage<BackStageArchiveSituationDTO>? {
        if (ObjectUtil.isNull(id)) {
            val dtoPage: IPage<BackStageArchiveSituationDTO> = Page(page.toLong(), size.toLong(), 0)
            dtoPage.setRecords(CollUtil.newArrayList())
            return dtoPage
        }
        val queryWrapper = QueryWrapper<ZhgdArchiveSituation>()
        queryWrapper.eq("parent_id", id)
        queryWrapper.isNotNull("file_name")
        if (StrUtil.isNotBlank(keyWord)) {
            queryWrapper.like("file_name", keyWord)
            queryWrapper.like("file_address", keyWord)
        }
        val pageRequest = Page<ZhgdArchiveSituation>(page.toLong(), size.toLong())
        val zhgdArchiveSituationPage: IPage<ZhgdArchiveSituation> = this.page(pageRequest, queryWrapper)
        // Convert to DTO
        val dtoList = zhgdArchiveSituationPage.records.stream()
            .map { (id1, fileName, _, _, fileAddress, createTime): ZhgdArchiveSituation ->
                val dto = BackStageArchiveSituationDTO(
                    id=id1,
                    fileName=fileName,
                    fileAddress=fileAddress,
                    uploadTime=DateUtil.format(createTime, "yyyy-MM-dd HH:mm:ss")
                )
                dto
            }.collect(Collectors.toList())
        val dtoPage: IPage<BackStageArchiveSituationDTO> =
            Page(page.toLong(), size.toLong(), zhgdArchiveSituationPage.total)
        dtoPage.records = dtoList
        return dtoPage
    }


    private fun writeToCatalogData(node: CatalogNode, parentId: Int?) {
        val zhgdArchiveSituation = ZhgdArchiveSituation()
        zhgdArchiveSituation.type = node.catalogName
        zhgdArchiveSituation.parentId = parentId
        zhgdArchiveSituationMapper!!.insert(zhgdArchiveSituation)
        val generatedId = zhgdArchiveSituation.id // 获取自动生成的主键ID
        for (child in node.children!!) {
            writeToCatalogData(child, generatedId)
        }
    }

}